查看原文
其他

const

鱼鹰 嵌入式软件进阶之路 2021-01-31

const:限定一个变量不允许改变,产生静态作用,const在一定程度上可以提高程序的安全性和可靠性。

const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。宏定义:

#define xxxx\ 

{\

        xxx\

}\

 

\后不能有任何字符,包括空格  最后的}\”需要最少空一行

struct xxxx  xxx = {.x1 = xxxxx,.x2 = xxxxx,.x3 = xxxxx};C99模式下可以使用该方式对结构体进行初始化,方便观察每一个变量的初始值。

声明为const的变量是不能被用户改变的,因为编译器会将该变量放在只读区,比如在KEIL开发平台下,声明为const的变量放在FLASH区,这样即使你使用取地址符 & 获取声明为 const 变量地址,并通过指针进行修改,虽然编译器不报错,但也是无法进行修改的,因为进行FLASH编程是有条件的。                

你会发现虽然P获取了N的地址,但因为N存放在FLAH中,所以通过指针也是无法改变N的值的。

可以看到N没有改变。但是编译器确实也没报错。但是如果你直接 N = 4 的话,肯定是报错的,因为你的N被申明为const

 

C语言深度剖析》中关于const的介绍发现和KEIL情况不一样。

KEIL中进行相关代码的编写,编译,最后可以看到如下结果:

(偶然巧合下才发现FLAH中也有一份拷贝,并且是RAM的复制源头)

这是仿真模式下两个地址的内容,一个存放在FLAH,一个存放在RAM中,并且当修改FLAH的内容之后(因为是软件仿真模式,可以直接修改值),复位重新运行,你会发现RAM的内容对应改变了(重新运行后,进入main函数之前,有一段拷贝代码,就是函数外申明的一些变量的初始化过程),这就说明,在STM32KEIL环境下,并不是《C语言深度剖析》中说的只有一份内存,而是每一个都有一个,申明为const情况跟使用宏定义的方式是一样的。

以下是Watch中的内容

但其实上面的结论是在使用 & N的地址获取后的结果(从上图可以看到p的值),实际上代码中如果没有获取N的地址时,情况又不一样了。

内存情况:

在删去获取N地址后的内存情况,可以发现N的值为0x2000470,和FLAH地址0x08000000一样。

这像一个地址。但通过Memory查看这个地址发现存放的不是5

根据ARM内核的知识可以知道,0x08000000地址存放的其实是栈顶指针,也就是说N存放的是栈顶指针吗?显然不是。

然后对.map地址映射文件进行搜索,你会发现,根本没有N的地址。这样说来,N在内存的位置对用户是不可见的,而是由编译器自动处理了。

那么有没有办法找到这个拷贝源头呢。之前我说过,先前能找到拷贝的源头纯属偶然,有没有什么方法可以找到呢?这其中的难点就是进入main函数之前的那段拷贝代码不是我们用户自己写的,而是C编译器自动处理的,怎么办?

这个时候就需要请出一个关键人物:数据观察点(关于数据观察点,将有专门的一小节详细说明)。

我们知道,不管如何,因为FLASH存放着变量初始值,然后在程序运行的时候才将FLASH中的值初始化到RAM中去,也就是我们使用的RAM变量,那么必然存在通过总线进行数据传输的过程,所以可以通过数据观察点的功能实现对地址的监控,虽然我们不知道FLAH的地址,但是我们知道RAM的地址,所以只要对变量i进行监控,就可以通过内核的寄存器找到FLASH地址了。如下:

这里的0x20000000就是iRAM地址,最终可以找到FLASH的位置:

由此可以知道,FLAH中也是有多个相同副本存在的。

因此可以得出结论,在STM32KEIL的环境下,《C语言深度剖析》对于两者的说法在这里不适用。

看了那么多,没有足够的基础是很难知道我在讲什么的,下面用一张图进行说明,希望可以解答你的疑惑。

希望你在看完这张图之后,再回过头看看前面的那些话,对你应该会有帮助的。

-----------------------------------------------------------------------------------2018/12/06  Osprey


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存